package com.yb0os1.combination.clac.expressionParser;

import com.yb0os1.combination.clac.expression.AddExpression;
import com.yb0os1.combination.clac.expression.DevisionExpression;
import com.yb0os1.combination.clac.expression.Expression;
import com.yb0os1.combination.clac.expression.MultiplyExpression;
import com.yb0os1.combination.clac.expression.NumberExpression;
import com.yb0os1.combination.clac.expression.SubstractExpression;
import com.yb0os1.combination.clac.expression.VariableExpression;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ExpressionParser {
    private String infixExpression;//中缀表达式
    private int pos = 0;
    public final Map<String, Integer> variables = new HashMap<>();

    public ExpressionParser(String infixExpression) {
        this.infixExpression = infixExpression;
    }

    /*中缀表达式转化为后转表达式
     * 转换的规则：
     * 1、左括号直接入栈，直到遇到右括号，将栈中的元素依次出栈并加入后缀表达式
     * 2、运算符优先级高的先入栈，优先级低的先出栈
     * 1+15*( 9+4+(1 + 5)) +6
     */
    private List<String> toSuffixExpression() {
        LinkedList<String> stack = new LinkedList<>();
        List<String> suffixExpression = new ArrayList<>();
        //遍历中缀表达式
        while (pos < infixExpression.length()) {
            char c = infixExpression.charAt(pos);
            if (Character.isDigit(c)) {
                StringBuilder stringBuilder = new StringBuilder();
                while (pos < infixExpression.length() && Character.isDigit(infixExpression.charAt(pos))) {
                    stringBuilder.append(infixExpression.charAt(pos));
                    pos++;
                }
                pos--;
                suffixExpression.add(stringBuilder.toString());
            } else if (Character.isLetter(c)) {
                StringBuilder stringBuilder = new StringBuilder();
                while (pos < infixExpression.length() && Character.isLetter(infixExpression.charAt(pos))) {
                    stringBuilder.append(infixExpression.charAt(pos));
                    pos++;
                }
                pos--;
                suffixExpression.add(stringBuilder.toString());
            } else if (c == '+' || c == '-') {
                if (isOperator(stack)) {
                    suffixExpression.add(stack.removeLast());
                }
                stack.add(c + "");
            } else if (c == '*' || c == '/') {
                String last = stack.getLast();
                while (!stack.isEmpty() && last.equals("*") || last.equals("/")) {
                    suffixExpression.add(stack.removeLast());
                }
                stack.add(c + "");
            } else if (c == '(') {
                stack.add(c + "");
            } else if (c == ')') {
                while (!stack.isEmpty() && !stack.getLast().equals("(")) {
                    suffixExpression.add(stack.removeLast());
                }
                stack.removeLast();
            } else if (c == ' ') {
                pos++;
                continue;
            } else {
                throw new RuntimeException("非法字符");
            }
            pos++;
        }
        //看看操作数栈里面是否还有元素
        while (!stack.isEmpty()) {
            suffixExpression.add(stack.removeLast());
        }
        return suffixExpression;
    }

    private boolean isOperator(LinkedList<String> stack) {
        if (stack.isEmpty()) return false;
        return Set.of("+", "-", "*", "/").contains(stack.getLast());
    }

    //计算传入的表达式
    public Expression calculate() {
        List<String> suffixExpression = toSuffixExpression();
        LinkedList<Expression> numberStack = new LinkedList<>();
        for (String s : suffixExpression) {
            if (s.equals("+")) {
                Expression right = numberStack.removeLast();
                Expression left = numberStack.removeLast();
                numberStack.addLast(new AddExpression(left, right));
            } else if (s.equals("-")) {
                Expression right = numberStack.removeLast();
                Expression left = numberStack.removeLast();
                numberStack.addLast(new SubstractExpression(left, right));
            } else if (s.equals("*")) {
                Expression right = numberStack.removeLast();
                Expression left = numberStack.removeLast();
                numberStack.addLast(new MultiplyExpression(left, right));
            } else if (s.equals("/")) {
                Expression right = numberStack.removeLast();
                Expression left = numberStack.removeLast();
                numberStack.addLast(new DevisionExpression(left, right));
            } else if (variables.containsKey(s)) {
                numberStack.addLast(new VariableExpression(s, variables.get(s)));
            } else {//数字
                numberStack.addLast(new NumberExpression(Integer.parseInt(s)));
            }
        }
        return numberStack.removeLast();
    }
}
